﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ACC.Data.Line;
using System.Net.Sockets;
using ACC.Data;
using ACC.Data.Comm;
using System.Collections;
using ACC.Util.Common;
using ACC.Data.Entities.Common;

namespace ACC.Device.Common
{
    public class ClientCommonDriver : ACCDriver
    {
        #region Param
        protected String IP = "127.0.0.1";
        protected UInt16 port = 4001;
        #endregion

        public ClientCommonDriver(LineDeviceCfg Cfg)
            : base(Cfg)
        {
            String[] ps = _Cfg.Param.Split(Constants.SEPARATOR.ToCharArray());
            IP = ps[0];
            if (ps.Length > 1)
                port = UInt16.Parse(ps[1]);
        }

        #region Driver Interface
        protected Thread BGThread = null;
        protected bool _IsRun = false;
        public bool IsRun
        {
            get { return _IsRun; }
        }

        public override void Connect()
        {
            if (null != BGThread)
                return;
            BGThread = new Thread(new ThreadStart(BGThreadProc));
            BGThread.Name = _Cfg.Name + " Terminal driver thread";
            _IsRun = true;
            BGThread.IsBackground = true;
            BGThread.Start();
        }
        public override void Disconnect()
        {
            if (null == BGThread)
                return;
            _IsRun = false;
            BGThread.Join(3000);
            DisconnectClient();
            BGThread = null;
        }
        #endregion

        #region Terminal Connect
        protected Socket socket;
        protected object lockObj = new object();
        private int connectTryNum = 0;

        protected virtual void ConnectClient()
        {
            lock (lockObj)
            {
                try
                {
                    if (Connected)
                        return;
                    if (socket == null)
                        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                    if (connectTryNum == 0)
                    {
                        logger.Info(string.Format("Connect to terminal: name={0}; IP={1}; port={2}", _Cfg.Name, IP, port));
                    }

                    if (connectTryNum++ >= 100)
                    {
                        connectTryNum = 0;
                    }

                    socket.Connect(IP, port);
                    SetConnected(true);
                }
                catch
                {
                }
            }
        }

        protected virtual void DisconnectClient()
        {
            lock (lockObj)
            {
                try
                {
                    logger.Info(string.Format("Disconnect with terminal: name={0}", _Cfg.Name));
                    if (!Connected)
                        return;
                    SetConnected(false);
                    try
                    {
                        if (socket != null)
                            socket.Close();
                    }
                    catch { }

                    socket = null;
                }
                catch
                {
                }
            }
        }

        public virtual void BGThreadProc()
        {
            byte[] buf = new byte[20480];
            int num = 0;
            int len;
            while (_IsRun)
            {
                try
                {
                    Thread.Sleep(100);
                }
                catch
                {
                }
                try
                {
                    if (!_IsRun)
                    {
                        break;
                    }
                    if (!Connected)
                    {
                        ConnectClient();

                        //if (Connected)
                        //{
                        //    FireSendClientConfig();
                        //}
                    }
                    if (Connected)
                    {
                        len = 0;
                        if (socket.Available > 0)
                        {
                            len = socket.Receive(buf);
                            if (len > 0)
                            {
                                String cmd = Encoding.UTF8.GetString(buf, 0, len);
                                DispatchCmd(cmd);
                            }
                        }
                        if (!_IsRun)
                        {
                            break;
                        }
                        if (_IsRun && ++num > 10 * 5)
                        {
                            num = 0;
                            SendAccCmd(ACCCmd.Heartbeat, "");
                        }
                    }
                }
                catch (Exception ex)
                {
                    loggerCmdInst.Info(ex.ToString());
                    DisconnectClient();
                }
            }
        }

        protected virtual void DispatchCmd(String cmd)
        {
            string[] cmds = cmd.Split(new string[] { "\\r" }, StringSplitOptions.None);
            if (cmds != null && cmds.Length > 0)
            {
                //处理每一个客户端命令
                foreach (string command in cmds)
                {
                    if (string.IsNullOrEmpty(command))
                        return;

                    TerminalRequest clientRequest = JsonHelper.ToObject<TerminalRequest>(command);
                    if (clientRequest != null)
                    {
                        switch (clientRequest.ClientCmd)
                        {
                            case TerminalCmd.Heartbeat:
                                logger.Debug("Receive heartbeat");
                                break;
                            case TerminalCmd.Connect:
                                FireSendClientConfig();
                                break;
                            default:
                                break;
                        }
                    }
                    else
                    {
                        logger.Debug("Unknown request:" + command);
                    }
                }
            }
        }

        public virtual int SendAccCmd(ACCCmd cmd, object param)
        {
            if (!Connected)
                return -1;
            try
            {
                lock (lockObj)
                {
                    byte[] buf = null;
                    ACCServerRequest serverRequest = new ACCServerRequest();
                    serverRequest.ServerCmd = cmd;
                    serverRequest.Parameters = param;

                    string requestString = JsonHelper.ToJson<ACCServerRequest>(serverRequest);
                    requestString += "\\r";

                    loggerCmdInst.Info(String.Format("Send: {0}: {1}", cmd, requestString));

                    buf = Encoding.UTF8.GetBytes(requestString);

                    if (buf != null && buf.Length > 0)
                    {
                        int len = socket.Send(buf);
                        return 0 <= len ? 0 : -1;
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Info(String.Format("Send {0} error: {1}", cmd, ex.ToString()));
                DisconnectClient();
            }

            return -1;
        }
        #endregion

        #region Terminal Command
        public delegate void ClientConfigHandle();
        public event ClientConfigHandle OnSendClientConfig;

        public virtual void FireSendClientConfig()
        {
            if (null == OnSendClientConfig)
                return;

            OnSendClientConfig();
        }
        #endregion

    }

}
